#pragma once

#include <iostream>
#include <string>
#include <memory>
#include "Log.hpp"
#include "Socket.hpp"
#include <cerrno>
#include <functional>
#include <sys/epoll.h>

using namespace LogMudule;

#define MAX 64

const int gdefaultfd = -1;

using handle_t = std::function<std::string(const std::string)>;

class EpollServer
{

public:
    EpollServer(int port, handle_t handle)
        : _port(port),
          _sock(std::make_shared<TcpSocket>(gdefaultfd)),
          _handle(handle),
          _epollfd(gdefaultfd)
    {
    }

    // 初始化
    void Init()
    {
        // 初始化套接字
        _sock->BuildTcpSocketMethod(_port);

        // 获取一个epoll模型
        _epollfd = epoll_create(256);
        if (_epollfd < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_create error ...";
            return;
        }

        // 将监听套接字添加到epoll
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _sock->Fd();
        int n = epoll_ctl(_epollfd, EPOLL_CTL_ADD, _sock->Fd(), &ev);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "epoll__ctl error ...";
            return;
        }
    }

    void Start()
    {
        while (true)
        {
            // 设置超时时间
            int timeout = -1;

            // 开始等待
            int n = epoll_wait(_epollfd, _ev_array, MAX, timeout);
            if (n > 0) // 有就绪的文件描述符
            {
                LOG(LogLevel::DEBUG) << "有就绪的文件描述符";
                DisposeFd(n);
            }
            else if (n == 0) // 没有就绪的文件描述符
            {
                LOG(LogLevel::DEBUG) << "时间到了，没有就绪的文件描述符";
                continue;
            }
            else
            {
                LOG(LogLevel::ERROR) << "poll error :" << errno;
                return;
            }
        }
    }

    void DisposeFd(int n)
    {
        for (int i = 0; i < n; ++i)
        {
            // 是监听文件描述符
            if (_ev_array[i].data.fd == _sock->Fd())
            {
                // 有新连接到来
                if (_ev_array[i].events & EPOLLIN)
                {
                    // 处理连接
                    Accept();
                }
            }
            else if (_ev_array[i].events & EPOLLIN) // 通信文件描述符
            {
                // 处理信息
                RS(_ev_array[i].data.fd);
            }
        }
    }

    void Accept()
    {

        InetAddr client;
        std::shared_ptr<Socket> sk = _sock->Accepter(&client);

        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sk->Fd();
        int n = epoll_ctl(_epollfd, EPOLL_CTL_ADD, sk->Fd(), &ev);
    }

    void RS(int fd)
    {
        std::string out;
        char buffer[1024];
        int n = read(fd, buffer, sizeof(buffer) - 1);
        if (n > 0) // 读到数据
        {
            buffer[n] = 0;
            out = buffer;
            LOG(LogLevel::DEBUG) << "buffer: " << out;

            std::string res = _handle(out);

            LOG(LogLevel::DEBUG) << "准备发送： " << fd;
            int n = send(fd, res.c_str(), res.size(), 0);
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "send err...";
            }
            LOG(LogLevel::DEBUG) << "send succeed...";
        }
        else if (n == 0) // 对端关闭
        {
            // 1. 从epoll中移除
            int m = epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, nullptr);
            if (m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll__ctl error ...";
                return;
            }
            // 2. 关闭fd
            close(fd);

            LOG(LogLevel::DEBUG) << "对端退出";
        }
        else
        {
            int m = epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, nullptr);
            // 关闭fd
            close(fd);
            LOG(LogLevel::ERROR) << "read err...";
            return;
        }
    }

    ~EpollServer()
    {
        _sock->Close();
        if(_epollfd > 0)
        {
            close(_epollfd);
        }
    }

private:
    uint16_t _port;                // 端口号
    std::shared_ptr<Socket> _sock; // socket
    int _epollfd;
    struct epoll_event _ev_array[MAX];
    handle_t _handle; // 回调函数
};